/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.bean.timer.interfaces;

import com.bean.timer.TimerEvent;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import javax.swing.JPanel;

/**
 *
 * @author sammael
 */
public abstract class AbstractTimer extends JPanel implements AbstractTimerControl,AbstractTimeView,AbstractTimerModel{

    public final String PARAM_NAME_LIMIT = "limit";
    public final String PARAM_NAME_TICK_LEN = "tickLen";
    public final String PARAM_NAME_COUNT = "tickcount";
    public final String PARAM_NAME_COUNTDOWN = "countdown";
    
    
    long limit;
    AtomicLong atomicElapsedTime;
    //volatile long currentTime;
    int tickLengh;
    int warningTicksCount;
    List<TimerListener> listeners = new ArrayList<>();
    boolean countingDown;
    ExecutorService executor = Executors.newCachedThreadPool();
    Clock clock = new Clock();

    public AbstractTimer() {
        Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {

            @Override
            public void run() {
               executor.shutdownNow();
               }
            })
        );
    }
    
    
    
    
    @Override
    public long getLimit() {
        return limit;
    }

    @Override
    public void setLimit(long limit) {
        long old = this.limit;
        this.limit = limit;
        propertyChange(PARAM_NAME_LIMIT, old, limit);
    }

    @Override
    public boolean isCountingDown() {
        return countingDown;
    }

    @Override
    public void setCountingDown(boolean countingDown) {
        boolean old = this.countingDown;
        this.countingDown = countingDown;
        propertyChange(PARAM_NAME_COUNTDOWN, old, countingDown);
    }

    
    public long elapsed() {
        return atomicElapsedTime.get();
    }
/*
    @Override
    private void setCurrentTime(long time) {
        throw new UnsupportedOperationException("Not supported yet.");
    }
*/
    @Override
    public void setWarningTicksCount(int ticks) {
        int old = this.warningTicksCount;
        this.warningTicksCount = ticks;
        propertyChange(PARAM_NAME_COUNT, old, ticks);
    }

    @Override
    public int getWarningTicksCount() {
        return warningTicksCount;
    }

    @Override
    public void setTickLength(int milisec) {
        int old = this.tickLengh;
        this.tickLengh = milisec;
        propertyChange(PARAM_NAME_TICK_LEN, old, milisec);
    }

    @Override
    public int getTickLength() {
        return tickLengh;
    }

    @Override
    public void addTimerListener(TimerListener listener) {
        listeners.add(listener);
    }

    @Override
    public void removeTimerListener(TimerListener listener) {
        listeners.remove(listener);
    
    }

    @Override
    public TimerListener[] getTimerListeners() {
        return (TimerListener[])listeners.toArray();
    }
    
    protected abstract void propertyChange(String param,boolean old,boolean fresh);
    protected abstract void propertyChange(String param,long old, long fresh);
    protected abstract void propertyChange(String param,int old,int fresh);
    protected abstract void propertyChange(String param,Object old,Object fresh);
    
    private synchronized void timerWariningEventOccured(){
        if(countingDown){
        long remaining = limit - atomicElapsedTime.get();
        int tick = (int)(remaining/tickLengh);
        if(tick<=this.warningTicksCount){
        TimerEvent event = new TimerEvent(this, tick, remaining);
        for(TimerListener l: listeners){
            l.onWarningTick(event);
        }
        }
        }
    }
    
    private synchronized void timerTimeoutEventOccured(){
        if(countingDown){
        long remaining = limit - atomicElapsedTime.get();
        //int tick = (int)(remaining/tickLengh);
        if(remaining<=0){
        TimerEvent event = new TimerEvent(this, Integer.MAX_VALUE, -1);
        for(TimerListener l: listeners){
            l.onTimeout(event);
        }
        }
        }
    }
    
    public final class Clock implements Runnable{
        private boolean stop;
        EventMonitor monitor = new EventMonitor();
        public void stop(){
            stop = true;
        }
        @Override
        public void run() {
            stop = false;
            while(!stop){
                try{
                    Thread.sleep(100);
                    atomicElapsedTime.getAndAdd(100);
                    executor.execute(monitor);
                }catch(InterruptedException e){
                }
            }
        }
        
    }
    public final class EventMonitor implements Runnable{

        @Override
        public void run() {
            timerTimeoutEventOccured();
            timerWariningEventOccured();
        }
        
    }
    
   
}
